package org.hackystat.sdsa.analyzer.control;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;

/**
 * Test interpretation of TDD episode inference.  
 * 
 * @author Hongbing Kou
 *
 */
public class TestTDDEpisodeInterpreter {
  /** The interpreter of TDD episode. */
  private TDDEpisodeInterpreter interpreter;
  
  private String testFirstBehavior = "test-first";
  private String refactoringBehavior = "refactoring";
  private String productionBehavior = "production";
  private String workingFile = "Zorro.java";
  private String testAddedString = "Some tests were added (1). ";
  
  /**
   * Test variable preparation.
   *
   */
  @Before public void setUp() {
    this.interpreter = new TDDEpisodeInterpreter(); 
  }
  
  /**
   * Test test-first episode type 1.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testTestFirstEpisodeType1() throws Exception {
    String args[] = {"1", "2", "3", "4", "5"};
    
    String output = this.interpreter.interpret(testFirstBehavior, "1", args);
    String expected = testAddedString +
                      "Then a compilation error occurred (2). " + 
                      "Then production code was edited (3). " +
                      "Then tests were run with failures (4). " + 
                      "Then production code was again edited (5).";
    assertEquals("Test test-first episode type 1", expected, output);        
  }

  /**
   * Test test-first episode type 2.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testTestFirstEpisodeType2() throws Exception {
    String args[] = {"1", "2", "3"};
    String output = this.interpreter.interpret(this.testFirstBehavior, "2", args);
    String expected = this.testAddedString + 
                      "Then a compilation error occurred (2). " +
                      "Then production code was added (3). " + 
                      "However, tests ran without failure.";
    assertEquals("Test test-first episode type 2", expected, output);
  }

  /**
   * Test test-first episode type 3.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testTestFirstEpisodeType3() throws Exception {
    String args[] = {"1", "2", "3"};
    String output = this.interpreter.interpret(this.testFirstBehavior, "3", args);
    String expected = this.testAddedString +
                      "Then tests ran with failures (2). " + 
                      "Then production code was edited (3). " +
                      "However, no compilation error occurred on test code.";
    assertEquals("Test test-first episode type 3", expected, output);
  }

  /**
   * Test test-first episode type 4.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testTestFirstEpisodeType4() throws Exception {
    String args[] = {"1", "2"};
    String output = this.interpreter.interpret(this.testFirstBehavior, "4", args);
    String expected = this.testAddedString +
                      "Then production code was edited (2). " +
                      "However, neither compilation error occurred nor " + 
                      "tests ran with failure.";
    assertEquals("Test test-first episode type 4", expected, output);
  }
  
  /**
   * Test refactoring episode type 1A.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testRefactoringEpisodeType1A() throws Exception {
    String args[] = {"1", "TestZorro.java"};
    String output = this.interpreter.interpret(this.refactoringBehavior, "1A", args);
    String expected = "Test file ``TestZorro.java\" was edited (1). " +     
                      "However, no new test or production methods were added.";
    assertEquals("Test refactoring episode type 1A", expected, output);
  }

  /**
   * Test refactoring episode type 1B.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testRefactoringEpisodeType1B() throws Exception {
    String args[] = {"1", "TestZorro.java"};
    String output = this.interpreter.interpret(refactoringBehavior, "1B", args);
    String expected = "Test file ``TestZorro.java\" was refactored (1).";
    assertEquals("Test refactoring episode type 1B", expected, output);
  }

  /**
   * Test refactoring episode type 2A.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testRefactoringEpisodeType2A() throws Exception {
    String args[] = {workingFile};
    String output = this.interpreter.interpret(this.refactoringBehavior, "2A", args);
    String expected = "The number of statements or methods of production file " + 
                       "``Zorro.java\" either decreased or remained stable.";
    assertEquals("Test refactoring episode type 2A", expected, output);
  }

  /**
   * Test refactoring episode type 2B.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testRefactoringEpisodeType2B() throws Exception {
    String args[] = {"1", workingFile};
    String output = this.interpreter.interpret(this.refactoringBehavior, "2B", args);
    String expected = "Production file ``Zorro.java\" was refactored (1).";
    assertEquals("Test refactoring episode type 2B", expected, output);
  }

  /**
   * Test refactoring episode type 2M with multip file edit.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testRefactoringEpisodeType2M() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret(this.refactoringBehavior, "2M", args);
    String expected = "Multiple product file edits. " + 
                      "However, neither method nor statement increases.";
    assertEquals("Test refactoring episode type 2M", expected, output);
  }
  
  /**
   * Test refactoring episode type 3.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testRefactoringEpisodeType3() throws Exception {
    String args[] = {"1", this.workingFile, "2", "TestZorro.java"};
    String output = this.interpreter.interpret(this.refactoringBehavior, "3", args);
    String expected = "Production file ``Zorro.java\" was edited (1). " + 
                      "Test file ``TestZorro.java\" was edited (2). " +  
                      "However, no new tests or production methods were added.";
        
    assertEquals("Test refactoring episode type 3", expected, output);
  }


  /**
   * Test test-last episode type 1.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testTestLastEpisodeType1() throws Exception {
    String args[] = {"6", "2"};
    String output = this.interpreter.interpret("test-last", "1", args);
    String expected = "Some tests were added (6) after production code was edited (2)."; 
    assertEquals("Test test-last episode type 1", expected, output);
  } 
  
  /**
   * Test test-addition episode type 1.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testTestAdditionEpisodeType1() throws Exception {
    String args[] = {"1"};
    String output = this.interpreter.interpret("test-addition", "1", args);
    String expected = "New tests were added (1), but no production code " + 
                      "was modified.";
    assertEquals("Test test-addition episode type 1", expected, output);
  }
  
  /**
   * Test test-addition episode type 2.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testTestAdditionEpisodeType2() throws Exception {
    String args[] = {"1", "2"};
    String output = this.interpreter.interpret("test-addition", "2", args);
    String expected = "New tests were added (1) and tests failed (2), " +
                      "but no production code was modified.";
    assertEquals("Test test-addition episode type 2", expected, output);
  }
  
  /**
   * Test regression episode type 1.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testRegressionEpisodeType1() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret("regression", "1", args);
    String expected = "";
    assertEquals("Test regression episode type 1", expected, output);
  }
  
  /**
   * Test regression episode type 2.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testRegressionEpisodeType2() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret("regression", "2", args);
    String expected = "Some tests ran with failures or they could not compile " + 
                      "due to test settings.";
    assertEquals("Test regression episode type 2", expected, output);
  }
  
  /**
   * Test production episode type 1.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testProductionEpisodeType1() throws Exception {
    String args[] = {this.workingFile};
    String output = this.interpreter.interpret(productionBehavior, "1", args);
    String expected = "More statements were added to production file ``Zorro.java\". " + 
                      "However, no test was added or edited.";
    assertEquals("Test production episode type 1", expected, output);
  }

  /**
   * Test production episode type 1 with multiple edits.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testProductionEpisodeType1M() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret(this.productionBehavior, "1M", args);
    String expected = "There are multiple product file edits, but no method change" + 
              ", and statement decreases.";
    assertEquals("Test production episode type 1M", expected, output);
  }

  /**
   * Test production episode type 2.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testProductionEpisodeType2() throws Exception {
    String args[] = {this.workingFile};
    String output = this.interpreter.interpret(this.productionBehavior, "2", args);
    String expected = "More methods were added to production file ``Zorro.java\". " + 
                      "However, no test was added or edited.";
    assertEquals("Test production episode type 2", expected, output);
  }

  /**
   * Test production episode type 2M.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testProductionEpisodeType2M() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret(this.productionBehavior, "2M", args);
    String expected = "There are multiple product file edits, and method increases" + 
                       ", but file size increases less than 100 bytes.";
    assertEquals("Test production episode type 2M", expected, output);
  }
  
  /**
   * Test production episode type 3.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testProductionEpisodeType3() throws Exception {
    String args[] = {this.workingFile};
    String output = this.interpreter.interpret(this.productionBehavior, "3", args);
    String expected = "More methods were added to production file ``Zorro.java\". " + 
                      "And more than 100 bytes of production code were added. " +  
                      "Furthermore, no test was added or edited.";
    assertEquals("Test production episode type 3", expected, output);
  }

  /**
   * Test production episode type 3M with multipl edits.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testProductionEpisodeType3M() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret(this.productionBehavior, "3M", args);
    String expected = "There are multiple product file edits, and method increases, " +
                      "and file size increases more than 100.";
    assertEquals("Test production episode type 3M", expected, output);
  }

  /**
   * Test long episode type 1.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testLongEpisodeType1() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret("long", "1", args);
    String expected = "It had too many activities before tests ran.";
    assertEquals("Test long episode type 1", expected, output);
  }

  /**
   * Test long episode type 2.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testLongEpisodeType2() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret("long", "2", args);
    String expected = "It took too much time before tests ran.";
    assertEquals("Test long episode type 2", expected, output);
  }
  
  /**
   * Test unknown episode type 1.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testUnknowType1() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret("unknown", "1", args);
    String expected = "No appropriate rules applied for this episode.";
    //String expected = "It did not end with successful unit test runs.";
    assertEquals("Test unknown episode type 1", expected, output);
  }
  
  /**
   * Test unknown episode type 2.
   * 
   * @throws Exception If error in testing.
   */
  @Test public void testUnknowType2() throws Exception {
    String args[] = {};
    String output = this.interpreter.interpret("unknown", "2", args);
    //String expected = "No appropriate rules applied for this episode";
    String expected = "It did not end with successful unit test runs.";
    assertEquals("Test unknown episode type 2", expected, output);
  }
}